package com.ggs.rg.ybjb.systemmodule.config;


import com.ggs.rg.ybjb.systemmodule.shiro.LoginFilter;
import com.ggs.rg.ybjb.systemmodule.shiro.ShiroSessionIdGenerator;
import com.ggs.rg.ybjb.systemmodule.shiro.ShiroSessionManager;
import com.ggs.rg.ybjb.systemmodule.shiro.UserRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;

/**
 * ClassName:ShiroConfig
 * Package:com.ggs.rg.config
 * Description:
 *             shiro配置类
 * @Date:2021/7/30 20:47
 * @Author:cxw
 */
@Configuration
public class ShiroConfig {

    private final String CACHE_KEY = "shiro:cache:";
    private final String SESSION_KEY = "shiro:session:";

    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.password}")
    private String password;

    /**
     * 加密配置类,在这里面配置加密方式，加密次数等,之后将该配置类设置到realm对象中才能生效
     * shiro会自动将前端传过来的密码根据这个类中配置的方式进行加密，
     * 之后再与数据库中拿到的加密密码字段进行对比
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法;
//        hashedCredentialsMatcher.setHashIterations(2);// 散列的次数，比如散列两次，相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }

    /**
     * 创建realm对象，第一步需要自定义类一个类实现AuthorizingRealm,里面定义了如何认证，授权
     */
    @Bean
    public UserRealm userRealm(){
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return userRealm;
    }

    /**
     * 第二步，声明DafaultWebSecurityManager，里面有SecurityManager 管理所有subject
     * 需要传入一个AuthorizingRealm类型或其子类型
     */
    @Bean
    public DefaultWebSecurityManager securityManager(UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联userRealm
        securityManager.setRealm(userRealm);
        //将自定义的会话管理器注册到安全管理器中
        securityManager.setSessionManager(sessionManager());
        //将自定义的redis缓存管理器注册到安全管理器中
        securityManager.setCacheManager(cacheManager());
        //绑定"记住我"功能
//        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     * 第三步ShiroFilterFactroyBean，需要传入一个DefaultWebSecurityManager对象
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        factoryBean.setSecurityManager(securityManager);
        //配置filter，解决认证失败或"没有登录访问需要登录了才能访问的请求"时返回login.jsp的问题
        Map<String, Filter> filters = factoryBean.getFilters();
        filters.put("authc",new LoginFilter());
        //添加内置过滤器（定义哪些页面要怎样的权限才能访问）
        /*
         * anon 无需认证就可以访问
         * authc: 必须认证才能访问（登录即可访问）
         * user:    必须拥有记住我功能才能使用
         * perms:    拥有对某个资源的权限才能访问
         * role:    拥有某个角色权限才能访问
         */
        //通过map定义要访问指定的url，就得有特定的权限，这是shiro中的重点，大型项目往往有几百行maps.put
        Map<String,String> maps = new HashMap<>();
        //无需认证就可以访问的请求
        maps.put("/user/login","anon");
        maps.put("/user/register","anon");
        maps.put("/admin/tac/getTAC","anon");
        maps.put("/user/goods/getGoodsByType","anon");
        maps.put("/user/goods/searchGoodsByName","anon");
        maps.put("/admin/goods/getGoodsMater","anon");

        //登录即可访问的请求
        maps.put("/user/logout","authc");

        //用户端
        //地址相关
        maps.put("/user/address/addAddress","authc");
        maps.put("/user/address/deleteAddressByAid","authc");
        maps.put("/user/address/updateAddressByAid","authc");
        maps.put("/user/address/getAddress","authc");

        //购物车相关
        maps.put("/user/shopping/addShopping","authc");
        maps.put("/user/shopping/selectShopping","authc");
        maps.put("/user/shopping/deleteShopping","authc");

        //订单相关
        maps.put("/user/shopping/addOrderByShopping","authc");
        maps.put("/user/order/getPaidOrdersByPhone","authc");
        maps.put("/user/order/getUnPaidOrdersByPhone","authc");
        maps.put("/user/order/getOrderDetailByOid","authc");
        maps.put("/user/order/payToMoney","authc");
        maps.put("/user/order/addOrder","authc");
        maps.put("/user/order/deleteOrdersByOid","authc");

        //用户相关
        maps.put("/user/userinfo/getUserInfoByPhone","authc");
        maps.put("/user/userinfo/updateUserInfo","authc");
        maps.put("/user/comment/addUserComment","authc");

        //管理端
        //公告模块
        maps.put("/admin/tac/addTAC","authc");
        maps.put("/admin/tac/deleTAC","authc");
        maps.put("/admin/tac/updateTAC","authc");

        //订单模块
        maps.put("/admin/order/getOrders","authc");
        maps.put("/admin/order/getPaidOrders","authc");
        maps.put("/admin/order/getUnpaidOrders","authc");
        maps.put("/admin/order/getOrderById","authc");
        maps.put("/admin/order/getOrderDetailById","authc");
        maps.put("/admin/order/getOrderCount","authc");
        maps.put("/admin/order/transactionAmount","authc");
        maps.put("/admin/order/getOrderData","authc");

        //用户相关
        maps.put("/admin/user/getUsers","authc");
        maps.put("/admin/user/getUserCount","authc");
        maps.put("/admin/user/getUserDeatilById","authc");

        //商品相关
        maps.put("/admin/goods/addGoods","authc");
        maps.put("/admin/goods/deleGoods","authc");
        maps.put("/admin/goods/deleMater","authc");
        maps.put("/admin/goods/getLike","authc");
        maps.put("/admin/goods/getGoods","authc");
        maps.put("/admin/goods/addCount","authc");
        maps.put("/admin/goods/selectBackorder","authc");
        maps.put("/admin/goods/selectBackorderLike","authc");

        //评论相关
        maps.put("/admin/comment/getComments","authc");
        maps.put("/admin/comment/addUserComment","authc");
        maps.put("/admin/comment/addComment","authc");

        //设置权限
        factoryBean.setFilterChainDefinitionMap(maps);
        return factoryBean;
    }

    //以下为将session与sessionId放入redis缓存中的配置

    /**
     * 1.redis的控制器，操作redis
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host + ":" + port);
        redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * 2.RedisSessionDAO
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager());
        sessionDAO.setSessionIdGenerator(sessionIdGenerator());
        sessionDAO.setKeyPrefix(SESSION_KEY);
        return sessionDAO;
    }

    /**
     * 3.DefaultWebSessionManager会话管理器
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        //自定义一个session管理器,并继承DefaultWebSessionManager
        ShiroSessionManager sessionManager = new ShiroSessionManager();
        sessionManager.setSessionIdCookie(sessionIdCookie());
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * 4.缓存管理器
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setKeyPrefix(CACHE_KEY);
        // 配置缓存的话要求放在session里面的实体类必须有个id标识
        redisCacheManager.setPrincipalIdFieldName("userId");
        return redisCacheManager;
    }

    @Bean
    public SimpleCookie sessionIdCookie(){
        //cookie名
        SimpleCookie sessionIdCookie = new SimpleCookie("shiro.session.id");
        //有效时间
        sessionIdCookie.setHttpOnly(true);
        sessionIdCookie.setMaxAge(3*1000*60);
//        sessionIdCookie.setDomain("/");
        return sessionIdCookie;
    }

    /**
     * SessionID生成器
     * @Author Sans
     * @CreateTime 2019/6/12 13:12
     */
    @Bean
    public ShiroSessionIdGenerator sessionIdGenerator(){
        return new ShiroSessionIdGenerator();
    }

    //以下为记住"我功能"的配置
    /**
     * cookie对象;会话Cookie模板 ,默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid或rememberMe，自定义
     * @return
     */
//    @Bean
//    public SimpleCookie rememberMeCookie(){
//        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
//        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//        //设为true后，只能通过http访问，javascript无法访问
//        //防止xss读取cookie
//        simpleCookie.setHttpOnly(true);
//        simpleCookie.setPath("/");
//        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
//        simpleCookie.setMaxAge(2592000);
//        return simpleCookie;
//    }

    /**
     * cookie管理对象;记住我功能,rememberMe管理器
     * @return
     */
//    @Bean
//    public CookieRememberMeManager rememberMeManager(){
//        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
//        cookieRememberMeManager.setCookie(rememberMeCookie());
//        return cookieRememberMeManager;
//    }
    /**
     * FormAuthenticationFilter 过滤器 过滤记住我
     * @return
     */
//    @Bean
//    public FormAuthenticationFilter formAuthenticationFilter(){
//        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
//        //对应前端的checkbox的name = rememberMe
//        formAuthenticationFilter.setRememberMeParam("rememberMe");
//        return formAuthenticationFilter;
//    }


}
